Cytoscape is a well-known bioinformatics tool for displaying
and exploring biological networks. R is a powerful
programming language and environment for statistical and exploratory
data analysis. RCy3 uses CyREST to communicate between
R and Cytoscape, allowing any graphs (e.g., iGraph,
graphNEL or dataframes) to be viewed, explored and manipulated with the
Cytoscape point-and-click visual interface. Thus, via RCy3, these two
quite different, quite useful bioinformatics software environments are
connected, mutually enhancing each other, providing new possibilities
for exploring biological data.
Installation
if(!"RCy3" %in% installed.packages()){
install.packages("BiocManager")
BiocManager::install("RCy3")
}
library(RCy3)
Prerequisites
In addition to this package (RCy3), you will need:
Big changes for RCy3
Welcome to RCy3 v2.0! If you have been using prior versions of RCy3,
then this is a big step. The fundamental change is that
CytoscapeConnection and CytoscapeWindow classes are no longer used.
These were pervasive arguments throughout the package in prior versions,
so practically every function signature has changed. However, this
change simplifies functions and in 99% of cases the old cw and cc args
can simply be deleted.
Some summary stats regarding the changes:
- 54 new functions (244 total)
- Other than function signatures,
- 44 functions are otherwise unchanged
- 37 functions have been annotated as deprecated
- 69 functions have been renamed to match updated docs
- 38 functions have been annotated as defunct
Going forward, this realignment will help us maintain the package in
sync with changes to critical upstream resources such as Cytoscape’s
CyREST API. It will also greatly simply script writing for users. We
hope you’ll stay onboard.
Where to start
We documented this upgrade in a few different ways to help you update
existing scripts you don’t want to leave behind and to adopt the new
conceptual model for RCy3 going forward.
Upgrading Existing Scripts
Here’s a wiki page from the main RCy3 repo that we will keep updated
with helpful tips. I’d recommend starting there:
NEWS
For a laundry list of changes made to the package in this transition
to v2.0, check out the NEWS from that release:
Example upgrades
It is always helpful to see examples. Here are a few of the most
common chunks of RCy3 usage, provided in BEFORE and
AFTER versions. If you have code similar to these in your
pre-v2.0 scripts, then try these suggested swaps.
Note: don’t run the BEFORE chunks with the latest
RCy3 package. These chunks are NOT meant to be run as a sequence, but
rather as isolated examples.
Example: displayGraph
BEFORE
g <- new ('graphNEL', edgemode='directed')
g <- graph::addNode ('A', g)
g <- graph::addNode ('B', g)
g <- graph::addNode ('C', g)
g <- graph::addEdge ('A', 'B', g)
g <- graph::addEdge ('B', 'C', g)
cw <- CytoscapeWindow ('vignette', graph=g, overwrite=TRUE)
displayGraph (cw)
layoutNetwork (cw, layout.name='grid')
AFTER
g <- new ('graphNEL', edgemode='directed')
g <- graph::addNode ('A', g)
g <- graph::addNode ('B', g)
g <- graph::addNode ('C', g)
g <- graph::addEdge ('A', 'B', g)
g <- graph::addEdge ('B', 'C', g)
net.suid <- createNetworkFromGraph (g, 'vignette')
- Note: the network SUID is returned rather than an R object
- Note: display and layout are done automatically
Example: cyPlot
BEFORE
node.df <- data.frame(id=c("A","B","C","D"),
stringsAsFactors=FALSE)
edge.df <- data.frame(source=c("A","A","A","C"),
target=c("B","C","D","D"),
interaction=c("inhibits","interacts","activates","interacts"), # optional
stringsAsFactors=FALSE)
g <- cyPlot(node.df, edge.df)
cw <- CytoscapeWindow("vignette2", g)
displayGraph(cw)
layoutNetwork(cw, "force-directed")
AFTER
node.df <- data.frame(id=c("A","B","C","D"),
stringsAsFactors=FALSE)
edge.df <- data.frame(source=c("A","A","A","C"),
target=c("B","C","D","D"),
interaction=c("inhibits","interacts","activates","interacts"), # optional
stringsAsFactors=FALSE)
net.suid <- createNetworkFromDataFrames (node.df, edge.df, "vignette2")
- Note: Same node and edge dataframes, except rather than having the
key columns being positionally defined (for cyPlot), they need to be
semantically defined using “id”, “source”, “target” and “interaction”
(for createNetworkFromDataFrames)
- Note: display and layout are done automatically
Example: loading nodeData
BEFORE
g <- initNodeAttribute (graph=g, attribute.name='moleculeType',
attribute.type='char',
default.value='undefined')
g <- initNodeAttribute (graph=g, 'lfc', 'numeric', 0.0)
nodeData (g, 'A', 'moleculeType') <- 'kinase'
nodeData (g, 'B', 'moleculeType') <- 'TF'
nodeData (g, 'C', 'moleculeType') <- 'cytokine'
nodeData (g, 'D', 'moleculeType') <- 'cytokine'
nodeData (g, 'A', 'lfc') <- -1.2
nodeData (g, 'B', 'lfc') <- 1.8
nodeData (g, 'C', 'lfc') <- 3.2
nodeData (g, 'D', 'lfc') <- 2.2
cw = setGraph (cw, g)
displayGraph (cw)
AFTER
node.data <- data.frame(id=c('A','B','C','D'),
moleculeType=c('kinase','TF','cytokine','cytokine'),
lfc=c(-1.2, 1.8, 3.2, 2.2),
stringsAsFactors = FALSE)
loadTableData(node.data, 'id')
- Note: simply compose a data.frame and then load it! No need to
initialize or alter local graphNEL object
- Note: stringsAsFactors = FALSE is important or else string values
won’t load
- Note: set and display are no longer needed
Example: setDefaults
BEFORE
setDefaultNodeShape (cw, 'OCTAGON')
setDefaultNodeColor (cw, '#AAFF88')
setDefaultNodeSize (cw, 80)
setDefaultNodeFontSize (cw, 40)
AFTER
setNodeShapeDefault ('OCTAGON')
setNodeColorDefault ('#AAFF88')
setNodeSizeDefault (80)
setNodeFontSizeDefault (40)
- Note: no more cw!
- Note: subtley renamed functions for consistency across package
(sorry!)
Example: Rule based mapping
BEFORE
attribute.values <- c ('kinase', 'TF', 'cytokine')
node.shapes <- c ('DIAMOND', 'TRIANGLE', 'RECTANGLE')
setNodeShapeRule (cw, 'moleculeType', attribute.values, node.shapes)
setNodeColorRule (cw, 'lfc', c (-3.0, 0.0, 3.0),
c ('#00AA00', '#00FF00', '#FFFFFF', '#FF0000', '#AA0000'),
mode='interpolate')
control.points = c (-1.2, 2.0, 4.0)
node.sizes = c (30, 40, 60, 80, 90)
setNodeSizeRule (cw, 'lfc', control.points, node.sizes, mode='interpolate')
AFTER
attribute.values <- c ('kinase', 'TF', 'cytokine')
node.shapes <- c ('DIAMOND', 'TRIANGLE', 'RECTANGLE')
setNodeShapeMapping('moleculeType', attribute.values, node.shapes)
setNodeColorMapping('lfc', c(-3.0, 0.0, 3.0),
c('#00AA00', '#00FF00', '#FFFFFF', '#FF0000', '#AA0000'))
control.points = c (-1.2, 2.0, 4.0)
node.sizes = c (30, 40, 60, 80, 90)
setNodeSizeMapping('lfc',control.points, node.sizes)
- Note: no more cw!
- Note: Rules are now Mappings (like they are called in
Cytoscape)
- Note: “interpolate” (a.k.a. “continuous”) is the default, so you can
leave it out
Example: Selecting nodes
BEFORE
selectNodes(cw, 'B')
nodes <- getSelectedNodes (cw)
selectFirstNeighborsOfSelectedNodes (cw)
AFTER
selectNodes('B', 'name') #or 'id'
nodes <- getSelectedNodes()
selectFirstNeighbors()
- Note: no more cw!
- Note: selectNodes is by node SUID by default, so you now have to
specify if using node name (or other alternative columns). This protects
against ambiguous selection.
- Note: selectNodes actually returns the selected nodes and edges
after the operation, so you could just do:
nodes <- selectNodes('B', 'name')$nodes
Example: Saving and exporting
BEFORE
saveImage(cw, "sample_image", "png", h = 800)
saveNetwork(cw, "sample_session", format = "cys")
AFTER
exportImage("sample_image", "png", h= 800)
saveSession("sample_session")
- Note: no more cw!
- Note: subtle renames to match actual operations and outputs
Going forward
And the next time you start a script from scratch, consider reviewing
the vignettes and other RCy3 scripts available online.
Repository of R scripts for Cytoscape, many using RCy3:
LS0tCnRpdGxlOiAiVXBncmFkaW5nIGV4aXN0aW5nIHNjcmlwdHMiCmF1dGhvcjogImJ5IEFsZXhhbmRlciBQaWNvLCBUYW5qYSBNdWV0emUsIFBhdWwgU2hhbm5vbiIKcGFja2FnZTogUkN5MwpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiAibm9uZSIKIyAgcGRmX2RvY3VtZW50OgojICAgIHRvYzogdHJ1ZSAKdmlnbmV0dGU6ID4KICAlXFZpZ25ldHRlSW5kZXhFbnRyeXswOC4gVXBncmFkaW5nIGV4aXN0aW5nIHNjcmlwdHMgfjE1IG1pbn0KICAlXFZpZ25ldHRlRW5naW5le2tuaXRyOjpybWFya2Rvd259CiAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KYGBge3IsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGV2YWw9RkFMU0UKKQpgYGAKCipDeXRvc2NhcGUqIGlzIGEgd2VsbC1rbm93biBiaW9pbmZvcm1hdGljcyB0b29sIGZvciBkaXNwbGF5aW5nIGFuZCBleHBsb3JpbmcgYmlvbG9naWNhbCBuZXR3b3Jrcy4KKipSKiogaXMgYSBwb3dlcmZ1bCBwcm9ncmFtbWluZyBsYW5ndWFnZSBhbmQgZW52aXJvbm1lbnQgZm9yIHN0YXRpc3RpY2FsIGFuZCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLgoqUkN5MyogdXNlcyBDeVJFU1QgdG8gY29tbXVuaWNhdGUgYmV0d2VlbiAqKlIqKiBhbmQgQ3l0b3NjYXBlLCBhbGxvd2luZyBhbnkgZ3JhcGhzIChlLmcuLCBpR3JhcGgsIGdyYXBoTkVMIG9yIGRhdGFmcmFtZXMpIHRvIGJlIHZpZXdlZCwgZXhwbG9yZWQgYW5kIG1hbmlwdWxhdGVkIHdpdGggdGhlIEN5dG9zY2FwZSBwb2ludC1hbmQtY2xpY2sgdmlzdWFsIGludGVyZmFjZS4gVGh1cywgdmlhIFJDeTMsIHRoZXNlIHR3byBxdWl0ZSBkaWZmZXJlbnQsIHF1aXRlIHVzZWZ1bCBiaW9pbmZvcm1hdGljcyBzb2Z0d2FyZSBlbnZpcm9ubWVudHMgYXJlIGNvbm5lY3RlZCwgbXV0dWFsbHkgZW5oYW5jaW5nIGVhY2ggb3RoZXIsIHByb3ZpZGluZyBuZXcgcG9zc2liaWxpdGllcyBmb3IgZXhwbG9yaW5nIGJpb2xvZ2ljYWwgZGF0YS4KCiMgSW5zdGFsbGF0aW9uCmBgYHtyfQppZighIlJDeTMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpewogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlJDeTMiKQp9CmxpYnJhcnkoUkN5MykKYGBgCgojIFByZXJlcXVpc2l0ZXMKSW4gYWRkaXRpb24gdG8gdGhpcyBwYWNrYWdlIChSQ3kzKSwgeW91IHdpbGwgbmVlZDoKCiAgKiAqKkN5dG9zY2FwZSAzLjYuMSoqIG9yIGdyZWF0ZXIsIHdoaWNoIGNhbiBiZSBkb3dubG9hZGVkIGZyb20gaHR0cDovL3d3dy5jeXRvc2NhcGUub3JnL2Rvd25sb2FkLnBocC4gU2ltcGx5IGZvbGxvdyB0aGUgaW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucyBvbiBzY3JlZW4uCgojIEJpZyBjaGFuZ2VzIGZvciBSQ3kzCldlbGNvbWUgdG8gUkN5MyB2Mi4wISBJZiB5b3UgaGF2ZSBiZWVuIHVzaW5nIHByaW9yIHZlcnNpb25zIG9mIFJDeTMsIHRoZW4gdGhpcyBpcwphIGJpZyBzdGVwLiBUaGUgZnVuZGFtZW50YWwgY2hhbmdlIGlzIHRoYXQgQ3l0b3NjYXBlQ29ubmVjdGlvbiBhbmQgQ3l0b3NjYXBlV2luZG93IGNsYXNzZXMgYXJlIG5vIGxvbmdlciB1c2VkLiBUaGVzZSB3ZXJlIHBlcnZhc2l2ZSBhcmd1bWVudHMgdGhyb3VnaG91dCB0aGUgcGFja2FnZSBpbiBwcmlvciB2ZXJzaW9ucywgc28gcHJhY3RpY2FsbHkgZXZlcnkgZnVuY3Rpb24gc2lnbmF0dXJlIGhhcyBjaGFuZ2VkLiBIb3dldmVyLCB0aGlzIGNoYW5nZSBzaW1wbGlmaWVzIGZ1bmN0aW9ucyBhbmQgaW4gOTklIG9mIGNhc2VzIHRoZSBvbGQgY3cgYW5kIGNjIGFyZ3MgY2FuIHNpbXBseSBiZSBkZWxldGVkLgoKKipTb21lIHN1bW1hcnkgc3RhdHMgcmVnYXJkaW5nIHRoZSBjaGFuZ2VzOioqCgogICogNTQgbmV3IGZ1bmN0aW9ucyAoMjQ0IHRvdGFsKQogICogT3RoZXIgdGhhbiBmdW5jdGlvbiBzaWduYXR1cmVzLCAKICAgICsgNDQgZnVuY3Rpb25zIGFyZSBvdGhlcndpc2UgdW5jaGFuZ2VkCiAgICArIDM3IGZ1bmN0aW9ucyBoYXZlIGJlZW4gYW5ub3RhdGVkIGFzIGRlcHJlY2F0ZWQKICAgICsgNjkgZnVuY3Rpb25zIGhhdmUgYmVlbiByZW5hbWVkIHRvIG1hdGNoIHVwZGF0ZWQgZG9jcwogICAgICArIDM4IGZ1bmN0aW9ucyBoYXZlIGJlZW4gYW5ub3RhdGVkIGFzIGRlZnVuY3QKCkdvaW5nIGZvcndhcmQsIHRoaXMgcmVhbGlnbm1lbnQgd2lsbCBoZWxwIHVzIG1haW50YWluIHRoZSBwYWNrYWdlIGluIHN5bmMgd2l0aCBjaGFuZ2VzIHRvIGNyaXRpY2FsIHVwc3RyZWFtIHJlc291cmNlcyBzdWNoIGFzIEN5dG9zY2FwZeKAmXMgQ3lSRVNUIEFQSS4gSXQgd2lsbCBhbHNvIGdyZWF0bHkgc2ltcGx5IHNjcmlwdCB3cml0aW5nIGZvciB1c2Vycy4gV2UgaG9wZSB5b3UnbGwgc3RheSBvbmJvYXJkLgoKIyBXaGVyZSB0byBzdGFydApXZSBkb2N1bWVudGVkIHRoaXMgdXBncmFkZSBpbiBhIGZldyBkaWZmZXJlbnQgd2F5cyB0byBoZWxwIHlvdSB1cGRhdGUgZXhpc3RpbmcgCnNjcmlwdHMgeW91IGRvbid0IHdhbnQgdG8gbGVhdmUgYmVoaW5kIGFuZCB0byBhZG9wdCB0aGUgbmV3IGNvbmNlcHR1YWwgbW9kZWwgCmZvciBSQ3kzIGdvaW5nIGZvcndhcmQuCgojIyBVcGdyYWRpbmcgRXhpc3RpbmcgU2NyaXB0cwpIZXJlJ3MgYSB3aWtpIHBhZ2UgZnJvbSB0aGUgbWFpbiBSQ3kzIHJlcG8gdGhhdCB3ZSB3aWxsIGtlZXAgdXBkYXRlZCB3aXRoIGhlbHBmdWwKdGlwcy4gSSdkIHJlY29tbWVuZCBzdGFydGluZyB0aGVyZToKCiAgKiBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL1JDeTMvd2lraS9VcGdyYWRpbmctRXhpc3RpbmctU2NyaXB0cyAKCiMjIE5FV1MKRm9yIGEgbGF1bmRyeSBsaXN0IG9mIGNoYW5nZXMgbWFkZSB0byB0aGUgcGFja2FnZSBpbiB0aGlzIHRyYW5zaXRpb24gdG8gdjIuMCwgCmNoZWNrIG91dCB0aGUgTkVXUyBmcm9tIHRoYXQgcmVsZWFzZToKCiAgKiBodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzLzMuNy9iaW9jL25ld3MvUkN5My9ORVdTCgojIEV4YW1wbGUgdXBncmFkZXMKSXQgaXMgYWx3YXlzIGhlbHBmdWwgdG8gc2VlIGV4YW1wbGVzLiBIZXJlIGFyZSBhIGZldyBvZiB0aGUgbW9zdCBjb21tb24gY2h1bmtzCm9mIFJDeTMgdXNhZ2UsIHByb3ZpZGVkIGluICpCRUZPUkUqIGFuZCAqQUZURVIqIHZlcnNpb25zLiBJZiB5b3UgaGF2ZSBjb2RlCnNpbWlsYXIgdG8gdGhlc2UgaW4geW91ciBwcmUtdjIuMCBzY3JpcHRzLCB0aGVuIHRyeSB0aGVzZSBzdWdnZXN0ZWQgc3dhcHMuCgoqKk5vdGU6IGRvbid0IHJ1biB0aGUgKkJFRk9SRSogY2h1bmtzIHdpdGggdGhlIGxhdGVzdCBSQ3kzIHBhY2thZ2UuIFRoZXNlIGNodW5rcyBhcmUgTk9UIG1lYW50IHRvIGJlIHJ1biBhcyBhIHNlcXVlbmNlLCBidXQgcmF0aGVyIGFzIGlzb2xhdGVkIGV4YW1wbGVzLioqCgojIyBFeGFtcGxlOiBkaXNwbGF5R3JhcGgKCipCRUZPUkUqCmBgYHtyLCBldmFsPUZBTFNFfQpnIDwtIG5ldyAoJ2dyYXBoTkVMJywgZWRnZW1vZGU9J2RpcmVjdGVkJykKZyA8LSBncmFwaDo6YWRkTm9kZSAoJ0EnLCBnKQpnIDwtIGdyYXBoOjphZGROb2RlICgnQicsIGcpCmcgPC0gZ3JhcGg6OmFkZE5vZGUgKCdDJywgZykKZyA8LSBncmFwaDo6YWRkRWRnZSAoJ0EnLCAnQicsIGcpCmcgPC0gZ3JhcGg6OmFkZEVkZ2UgKCdCJywgJ0MnLCBnKQpjdyA8LSBDeXRvc2NhcGVXaW5kb3cgKCd2aWduZXR0ZScsIGdyYXBoPWcsIG92ZXJ3cml0ZT1UUlVFKQpkaXNwbGF5R3JhcGggKGN3KQpsYXlvdXROZXR3b3JrIChjdywgbGF5b3V0Lm5hbWU9J2dyaWQnKQpgYGAKCipBRlRFUioKYGBge3J9CmcgPC0gbmV3ICgnZ3JhcGhORUwnLCBlZGdlbW9kZT0nZGlyZWN0ZWQnKQpnIDwtIGdyYXBoOjphZGROb2RlICgnQScsIGcpCmcgPC0gZ3JhcGg6OmFkZE5vZGUgKCdCJywgZykKZyA8LSBncmFwaDo6YWRkTm9kZSAoJ0MnLCBnKQpnIDwtIGdyYXBoOjphZGRFZGdlICgnQScsICdCJywgZykKZyA8LSBncmFwaDo6YWRkRWRnZSAoJ0InLCAnQycsIGcpCm5ldC5zdWlkIDwtIGNyZWF0ZU5ldHdvcmtGcm9tR3JhcGggKGcsICd2aWduZXR0ZScpCmBgYAoKKiBOb3RlOiB0aGUgbmV0d29yayBTVUlEIGlzIHJldHVybmVkIHJhdGhlciB0aGFuIGFuIFIgb2JqZWN0IAoqIE5vdGU6IGRpc3BsYXkgYW5kIGxheW91dCBhcmUgZG9uZSBhdXRvbWF0aWNhbGx5CgojIyBFeGFtcGxlOiBjeVBsb3QKKkJFRk9SRSoKYGBge3J9Cm5vZGUuZGYgPC0gZGF0YS5mcmFtZShpZD1jKCJBIiwiQiIsIkMiLCJEIiksCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKZWRnZS5kZiA8LSBkYXRhLmZyYW1lKHNvdXJjZT1jKCJBIiwiQSIsIkEiLCJDIiksCiAgICAgICAgICAgdGFyZ2V0PWMoIkIiLCJDIiwiRCIsIkQiKSwKICAgICAgICAgICBpbnRlcmFjdGlvbj1jKCJpbmhpYml0cyIsImludGVyYWN0cyIsImFjdGl2YXRlcyIsImludGVyYWN0cyIpLCAgIyBvcHRpb25hbAogICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCmcgPC0gY3lQbG90KG5vZGUuZGYsIGVkZ2UuZGYpCmN3IDwtIEN5dG9zY2FwZVdpbmRvdygidmlnbmV0dGUyIiwgZykKZGlzcGxheUdyYXBoKGN3KQpsYXlvdXROZXR3b3JrKGN3LCAiZm9yY2UtZGlyZWN0ZWQiKQpgYGAKCipBRlRFUioKYGBge3J9Cm5vZGUuZGYgPC0gZGF0YS5mcmFtZShpZD1jKCJBIiwiQiIsIkMiLCJEIiksCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKZWRnZS5kZiA8LSBkYXRhLmZyYW1lKHNvdXJjZT1jKCJBIiwiQSIsIkEiLCJDIiksCiAgICAgICAgICAgdGFyZ2V0PWMoIkIiLCJDIiwiRCIsIkQiKSwKICAgICAgICAgICBpbnRlcmFjdGlvbj1jKCJpbmhpYml0cyIsImludGVyYWN0cyIsImFjdGl2YXRlcyIsImludGVyYWN0cyIpLCAgIyBvcHRpb25hbAogICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCm5ldC5zdWlkIDwtIGNyZWF0ZU5ldHdvcmtGcm9tRGF0YUZyYW1lcyAobm9kZS5kZiwgZWRnZS5kZiwgInZpZ25ldHRlMiIpCmBgYAoKKiBOb3RlOiBTYW1lIG5vZGUgYW5kIGVkZ2UgZGF0YWZyYW1lcywgZXhjZXB0IHJhdGhlciB0aGFuIGhhdmluZyB0aGUga2V5IGNvbHVtbnMgYmVpbmcgcG9zaXRpb25hbGx5IGRlZmluZWQgKGZvciBjeVBsb3QpLCB0aGV5IG5lZWQgdG8gYmUgc2VtYW50aWNhbGx5IGRlZmluZWQgdXNpbmcgImlkIiwgInNvdXJjZSIsICJ0YXJnZXQiIGFuZCAiaW50ZXJhY3Rpb24iIChmb3IgY3JlYXRlTmV0d29ya0Zyb21EYXRhRnJhbWVzKQoqIE5vdGU6IGRpc3BsYXkgYW5kIGxheW91dCBhcmUgZG9uZSBhdXRvbWF0aWNhbGx5CgojIyBFeGFtcGxlOiBsb2FkaW5nIG5vZGVEYXRhIAoqQkVGT1JFKgpgYGB7ciwgZXZhbD1GQUxTRX0KZyA8LSBpbml0Tm9kZUF0dHJpYnV0ZSAoZ3JhcGg9ZywgIGF0dHJpYnV0ZS5uYW1lPSdtb2xlY3VsZVR5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRlLnR5cGU9J2NoYXInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdC52YWx1ZT0ndW5kZWZpbmVkJykKZyA8LSBpbml0Tm9kZUF0dHJpYnV0ZSAoZ3JhcGg9ZywgICdsZmMnLCAnbnVtZXJpYycsIDAuMCkKbm9kZURhdGEgKGcsICdBJywgJ21vbGVjdWxlVHlwZScpIDwtICdraW5hc2UnCm5vZGVEYXRhIChnLCAnQicsICdtb2xlY3VsZVR5cGUnKSA8LSAnVEYnCm5vZGVEYXRhIChnLCAnQycsICdtb2xlY3VsZVR5cGUnKSA8LSAnY3l0b2tpbmUnCm5vZGVEYXRhIChnLCAnRCcsICdtb2xlY3VsZVR5cGUnKSA8LSAnY3l0b2tpbmUnCm5vZGVEYXRhIChnLCAnQScsICdsZmMnKSA8LSAtMS4yCm5vZGVEYXRhIChnLCAnQicsICdsZmMnKSA8LSAxLjgKbm9kZURhdGEgKGcsICdDJywgJ2xmYycpIDwtIDMuMgpub2RlRGF0YSAoZywgJ0QnLCAnbGZjJykgPC0gMi4yCmN3ID0gc2V0R3JhcGggKGN3LCBnKQpkaXNwbGF5R3JhcGggKGN3KSAKYGBgCgoqQUZURVIqCmBgYHtyfQpub2RlLmRhdGEgPC0gZGF0YS5mcmFtZShpZD1jKCdBJywnQicsJ0MnLCdEJyksIAogICAgICAgICAgICAgICAgICAgICAgICBtb2xlY3VsZVR5cGU9Yygna2luYXNlJywnVEYnLCdjeXRva2luZScsJ2N5dG9raW5lJyksCiAgICAgICAgICAgICAgICAgICAgICAgIGxmYz1jKC0xLjIsIDEuOCwgMy4yLCAyLjIpLAogICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmxvYWRUYWJsZURhdGEobm9kZS5kYXRhLCAnaWQnKQpgYGAKCiogTm90ZTogc2ltcGx5IGNvbXBvc2UgYSBkYXRhLmZyYW1lIGFuZCB0aGVuIGxvYWQgaXQhIE5vIG5lZWQgdG8gaW5pdGlhbGl6ZSBvciBhbHRlciBsb2NhbCBncmFwaE5FTCBvYmplY3QKKiBOb3RlOiBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UgaXMgaW1wb3J0YW50IG9yIGVsc2Ugc3RyaW5nIHZhbHVlcyB3b24ndCBsb2FkCiogTm90ZTogc2V0IGFuZCBkaXNwbGF5IGFyZSBubyBsb25nZXIgbmVlZGVkCgoKCiMjIEV4YW1wbGU6IHNldERlZmF1bHRzCipCRUZPUkUqCmBgYHtyLCBldmFsPUZBTFNFfQpzZXREZWZhdWx0Tm9kZVNoYXBlIChjdywgJ09DVEFHT04nKQpzZXREZWZhdWx0Tm9kZUNvbG9yIChjdywgJyNBQUZGODgnKQpzZXREZWZhdWx0Tm9kZVNpemUgIChjdywgODApCnNldERlZmF1bHROb2RlRm9udFNpemUgKGN3LCA0MCkKYGBgCgoqQUZURVIqCmBgYHtyfQpzZXROb2RlU2hhcGVEZWZhdWx0ICgnT0NUQUdPTicpCnNldE5vZGVDb2xvckRlZmF1bHQgKCcjQUFGRjg4JykKc2V0Tm9kZVNpemVEZWZhdWx0ICAoODApCnNldE5vZGVGb250U2l6ZURlZmF1bHQgKDQwKQpgYGAKCiogTm90ZTogbm8gbW9yZSBjdyEKKiBOb3RlOiBzdWJ0bGV5IHJlbmFtZWQgZnVuY3Rpb25zIGZvciBjb25zaXN0ZW5jeSBhY3Jvc3MgcGFja2FnZSAoc29ycnkhKQoKCiMjIEV4YW1wbGU6IFJ1bGUgYmFzZWQgbWFwcGluZwoqQkVGT1JFKgpgYGB7ciwgZXZhbD1GQUxTRX0KYXR0cmlidXRlLnZhbHVlcyA8LSBjICgna2luYXNlJywgICdURicsICAgICAgICdjeXRva2luZScpCm5vZGUuc2hhcGVzICAgICAgPC0gYyAoJ0RJQU1PTkQnLCAnVFJJQU5HTEUnLCAnUkVDVEFOR0xFJykKc2V0Tm9kZVNoYXBlUnVsZSAoY3csICdtb2xlY3VsZVR5cGUnLCBhdHRyaWJ1dGUudmFsdWVzLCBub2RlLnNoYXBlcykKc2V0Tm9kZUNvbG9yUnVsZSAoY3csICdsZmMnLCBjICgtMy4wLCAwLjAsIDMuMCksCiAgICAgICAgICAgICAgICAgIGMgKCcjMDBBQTAwJywgJyMwMEZGMDAnLCAnI0ZGRkZGRicsICcjRkYwMDAwJywgJyNBQTAwMDAnKSwKICAgICAgICAgICAgICAgICAgbW9kZT0naW50ZXJwb2xhdGUnKQpjb250cm9sLnBvaW50cyA9IGMgKC0xLjIsIDIuMCwgNC4wKQpub2RlLnNpemVzICAgICA9IGMgKDMwLCA0MCwgNjAsIDgwLCA5MCkKc2V0Tm9kZVNpemVSdWxlIChjdywgJ2xmYycsIGNvbnRyb2wucG9pbnRzLCBub2RlLnNpemVzLCBtb2RlPSdpbnRlcnBvbGF0ZScpCmBgYAoKKkFGVEVSKgpgYGB7cn0KYXR0cmlidXRlLnZhbHVlcyA8LSBjICgna2luYXNlJywgICdURicsICAgICAgICdjeXRva2luZScpCm5vZGUuc2hhcGVzICAgICAgPC0gYyAoJ0RJQU1PTkQnLCAnVFJJQU5HTEUnLCAnUkVDVEFOR0xFJykKc2V0Tm9kZVNoYXBlTWFwcGluZygnbW9sZWN1bGVUeXBlJywgYXR0cmlidXRlLnZhbHVlcywgbm9kZS5zaGFwZXMpCnNldE5vZGVDb2xvck1hcHBpbmcoJ2xmYycsIGMoLTMuMCwgMC4wLCAzLjApLAogICAgICAgICAgICAgICAgICAgIGMoJyMwMEFBMDAnLCAnIzAwRkYwMCcsICcjRkZGRkZGJywgJyNGRjAwMDAnLCAnI0FBMDAwMCcpKQpjb250cm9sLnBvaW50cyA9IGMgKC0xLjIsIDIuMCwgNC4wKQpub2RlLnNpemVzICAgICA9IGMgKDMwLCA0MCwgNjAsIDgwLCA5MCkKc2V0Tm9kZVNpemVNYXBwaW5nKCdsZmMnLGNvbnRyb2wucG9pbnRzLCBub2RlLnNpemVzKQpgYGAKCiogTm90ZTogbm8gbW9yZSBjdyEKKiBOb3RlOiBSdWxlcyBhcmUgbm93IE1hcHBpbmdzIChsaWtlIHRoZXkgYXJlIGNhbGxlZCBpbiBDeXRvc2NhcGUpCiogTm90ZTogImludGVycG9sYXRlIiAoYS5rLmEuICJjb250aW51b3VzIikgaXMgdGhlIGRlZmF1bHQsIHNvIHlvdSBjYW4gbGVhdmUgaXQgb3V0CgoKIyMgRXhhbXBsZTogU2VsZWN0aW5nIG5vZGVzIAoqQkVGT1JFKgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2VsZWN0Tm9kZXMoY3csICdCJykKbm9kZXMgPC0gZ2V0U2VsZWN0ZWROb2RlcyAoY3cpCnNlbGVjdEZpcnN0TmVpZ2hib3JzT2ZTZWxlY3RlZE5vZGVzIChjdykKYGBgCgoqQUZURVIqCmBgYHtyfQpzZWxlY3ROb2RlcygnQicsICduYW1lJykgI29yICdpZCcKbm9kZXMgPC0gZ2V0U2VsZWN0ZWROb2RlcygpCnNlbGVjdEZpcnN0TmVpZ2hib3JzKCkKYGBgCgoqIE5vdGU6IG5vIG1vcmUgY3chCiogTm90ZTogc2VsZWN0Tm9kZXMgaXMgYnkgbm9kZSBTVUlEIGJ5IGRlZmF1bHQsIHNvIHlvdSBub3cgaGF2ZSB0byBzcGVjaWZ5IGlmIHVzaW5nIG5vZGUgbmFtZSAob3Igb3RoZXIgYWx0ZXJuYXRpdmUgY29sdW1ucykuIFRoaXMgcHJvdGVjdHMgYWdhaW5zdCBhbWJpZ3VvdXMgc2VsZWN0aW9uLgoqIE5vdGU6IHNlbGVjdE5vZGVzIGFjdHVhbGx5IHJldHVybnMgdGhlIHNlbGVjdGVkIG5vZGVzIGFuZCBlZGdlcyBhZnRlciB0aGUgb3BlcmF0aW9uLCBzbyB5b3UgY291bGQganVzdCBkbzogCmBgYHtyfQpub2RlcyA8LSBzZWxlY3ROb2RlcygnQicsICduYW1lJykkbm9kZXMKYGBgCgojIyBFeGFtcGxlOiBTYXZpbmcgYW5kIGV4cG9ydGluZwoqQkVGT1JFKgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2F2ZUltYWdlKGN3LCAic2FtcGxlX2ltYWdlIiwgInBuZyIsIGggPSA4MDApCnNhdmVOZXR3b3JrKGN3LCAic2FtcGxlX3Nlc3Npb24iLCBmb3JtYXQgPSAiY3lzIikKYGBgCgoqQUZURVIqCmBgYHtyfQpleHBvcnRJbWFnZSgic2FtcGxlX2ltYWdlIiwgInBuZyIsIGg9IDgwMCkKc2F2ZVNlc3Npb24oInNhbXBsZV9zZXNzaW9uIikKYGBgCgoqIE5vdGU6IG5vIG1vcmUgY3chCiogTm90ZTogc3VidGxlIHJlbmFtZXMgdG8gbWF0Y2ggYWN0dWFsIG9wZXJhdGlvbnMgYW5kIG91dHB1dHMKCgojIEdvaW5nIGZvcndhcmQKQW5kIHRoZSBuZXh0IHRpbWUgeW91IHN0YXJ0IGEgc2NyaXB0IGZyb20gc2NyYXRjaCwgY29uc2lkZXIgcmV2aWV3aW5nIHRoZSAKdmlnbmV0dGVzIGFuZCBvdGhlciBSQ3kzIHNjcmlwdHMgYXZhaWxhYmxlIG9ubGluZS4KCmBgYHtyfQpicm93c2VWaWduZXR0ZXMoJ1JDeTMnKQpgYGAKClJlcG9zaXRvcnkgb2YgUiBzY3JpcHRzIGZvciBDeXRvc2NhcGUsIG1hbnkgdXNpbmcgUkN5MzoKCiAgKiBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL2N5dG9zY2FwZS1hdXRvbWF0aW9uL3RyZWUvbWFzdGVyL2Zvci1zY3JpcHRlcnMvUg==